// EventLoop相关单元测试
#include "xlcomm/net/event_loop.h"

#include <sys/timerfd.h>
#include <unistd.h>

#include <iostream>
#include <thread>

#include "xlcomm/base/async_log.h"
#include "xlcomm/base/logger.h"
#include "xlcomm/base/sync_log.h"
#include "xlcomm/net/detail/acceptor.h"
#include "xlcomm/net/detail/event_loop_thread_pool.h"
#include "xlcomm/net/detail/timer.h"
#include "xlcomm/net/event_loop_thread.h"
#include "xlcomm/net/inet_address.h"
#include "xlcomm/net/tcp_server.h"
using namespace xlcomm;

#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK
#include "boost/test/unit_test.hpp"

// #define TEMP_DONOT_RUN  // 暂时不运行
#define CHECK_ABORT 0

#ifndef TEMP_DONOT_RUN
#if (CHECK_ABORT == 1)
BOOST_AUTO_TEST_CASE(EventLoop_abort) {
  std::cout << "-------------EventLoop_abort\n";
  net::EventLoop event_loop;
#if 1
  std::cout << "check run Loop in different thread.\n";
  std::thread thr1([&event_loop]() { event_loop.Loop(); });
  thr1.join();
#else
  std::cout << "check one thread create two event loop.\n";
  net::EventLoop event_loop2;
#endif
}
#endif  // CHECK_ABORT

BOOST_AUTO_TEST_CASE(EventLoop_create_destruct) {
  std::cout << "-------------EventLoop_create_destruct\n";
  // 同一线程构造、析构
  std::cout << "test loop1\n";
  std::thread thr1([]() {
    net::EventLoop event_loop1;
    // event_loop1.Loop();
    std::cout << "current thread's event loop:"
              << net::EventLoop::GetEventLoopOfCurrentThread() << std::endl;
  });
  thr1.join();

  // 线程A构造、运行，线程B退出，线程A析构
  std::cout << "test loop2\n";
  net::EventLoop event_loop2;

  std::thread thr2([&event_loop2]() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    event_loop2.Quit();
    std::cout << "loop2 quit\n";
  });

#if 0  // 测试先调Quit()后调Loop()
  std::this_thread::sleep_for(std::chrono::seconds(2));
#endif
  event_loop2.Loop();  // 阻塞
  std::cout << "current thread's event loop:"
            << net::EventLoop::GetEventLoopOfCurrentThread() << std::endl;

  thr2.join();
}

#if 0
BOOST_AUTO_TEST_CASE(EventLoop_wrong_use) {
  std::cout << "-------------EventLoop_wrong_use\n";

  // 一个线程构造、运行、析构，另一个线程退出
  using EventLoopPtr = net::EventLoop*;
  EventLoopPtr loop2;
  std::thread thr2([&loop2]() {
    net::EventLoop event_loop2;
    loop2 = &event_loop2;
    event_loop2.Loop();
    std::cout << "current thread's event loop:"
              << net::EventLoop::GetEventLoopOfCurrentThread()
              << std::endl;
  });

  std::thread thr1([]() {
    int a = 0;
    int b = a + 1;
    std::cout << "&a:" << &a << " &b:" << &b << std::endl;
  });
  thr1.join();

  if (loop2) {  // loop已经析构了！结果不可预期
    loop2->Quit();
  } else {
    std::cout << "loop never quit!\n";
  }
  std::cout << "loop quit\n";
  thr2.join();
}
#endif

BOOST_AUTO_TEST_CASE(SetLogLevel) {
  std::cout << "-------------SetLogLevel\n";
  Logger::SetLogLevel(Logger::kTrace);
}

net::EventLoop* g_timer_loop;
int g_timerfd;

void AddTimer() {
  itimerspec howlong;
  memset(&howlong, 0, sizeof howlong);
  howlong.it_value.tv_sec = 3;
  howlong.it_interval.tv_sec = 6;
  timerfd_settime(g_timerfd, 0, &howlong, nullptr);
}

void timer_func(Timestamp now) {
  char buff[1024];
  // 水平触发模式下，如果不read就会一直触发
  ssize_t n = ::read(g_timerfd, buff, sizeof(buff));
  printf("timestamp:%lu  read %ld bytes\n", now, n);
#if 1
  g_timer_loop->Quit();
#endif
}

BOOST_AUTO_TEST_CASE(EventLoop_loop) {
  std::cout << "-------------EventLoop_loop\n";
  net::EventLoop loop;
  g_timer_loop = &loop;

  g_timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
  if (g_timerfd < 0) {
    printf("timerfd_create error\n");
    return;
  }

  net::Channel channel(&loop, g_timerfd);
  channel.EnableReading();
  channel.set_read_callback(timer_func);

  AddTimer();

  loop.Loop();

  channel.DisableAll();
  channel.Remove();
  ::close(g_timerfd);
}

void timer_cb() {}

BOOST_AUTO_TEST_CASE(Timer_sequence) {
  std::cout << "-------------Timer_sequence\n";
  Timestamp now = DateTime::NowTimestamp();
  const int num = 10;
  std::thread thr1([&now]() {
    for (int i = 0; i < num; ++i) {
      net::Timer timer(timer_cb, now, 0);
      std::cout << "timer.sequence() = " << timer.sequence() << std::endl;
    }
  });
  std::thread thr2([&now]() {
    for (int i = 0; i < num; ++i) {
      net::Timer timer(timer_cb, now, 0);
      std::cout << "timer.sequence() = " << timer.sequence() << std::endl;
    }
  });
  std::thread thr3([&now]() {
    for (int i = 0; i < num; ++i) {
      net::Timer timer(timer_cb, now, 0);
      std::cout << "timer.sequence() = " << timer.sequence() << std::endl;
    }
  });
  thr1.join();
  thr2.join();
  thr3.join();
}

void RunAtFunc() { LOG_INFO << __FUNCTION__; }
void RunEveryFunc() { LOG_INFO << __FUNCTION__; }

BOOST_AUTO_TEST_CASE(EventLoop_timer) {
  std::cout << "-------------EventLoop_timer\n";
  Logger::SetLogLevel(Logger::kInfo);
  net::EventLoop loop;

  loop.RunInLoop([]() { LOG_INFO << "test RunInLoop"; });

  std::thread thr1([&loop]() {
    Timestamp now = DateTime::NowTimestamp();
    loop.RunAt(now + 1 * 1000 * 1000, RunAtFunc);
  });

  std::thread thr2([&loop]() {
    loop.RunAfter(6.5, std::bind(&net::EventLoop::Quit, &loop));
    loop.QueueInLoop([]() { LOG_INFO << "test QueueInLoop"; });
  });

  loop.RunEvery(2, RunEveryFunc);

  thr1.join();
  thr2.join();

  loop.Loop();
  std::cout << "EventLoop_timer return\n";
}
#endif  // TEMP_DONOT_RUN

void RunEveryFunc2() { LOG_INFO << __FUNCTION__; }

void InitCallback(net::EventLoop* loop) {
  std::cout << loop << " InitCallback\n";
}

BOOST_AUTO_TEST_CASE(EventLoopThread) {
  std::cout << "-------------EventLoopThread\n";

  net::EventLoopThread evloop_thread("evloop_thread1", InitCallback);
  net::EventLoop* loop = evloop_thread.StartLoop();
  std::cout << loop << " StartLoop\n";
  loop->RunEvery(2, RunEveryFunc2);

  net::EventLoopThread evloop_thread2("evloop_thread2", InitCallback);
  net::EventLoop* loop2 = evloop_thread2.StartLoop();
  std::cout << loop2 << " StartLoop\n";
  loop2->RunEvery(1, RunEveryFunc2);

  std::this_thread::sleep_for(std::chrono::seconds(5));
}

BOOST_AUTO_TEST_CASE(EventLoopThreadPool) {
  std::cout << "-------------EventLoopThreadPool\n";
  net::EventLoop loop;

  net::EventLoopThreadPool thread_pool(&loop, "evloop_thread_pool");
  thread_pool.Start(3);
  thread_pool.GetLoopForHash(3);

  // loop.RunAfter(5, std::bind(&net::EventLoop::Quit, &loop));
}

BOOST_AUTO_TEST_CASE(InetAddress) {
  std::cout << "-------------InetAddress\n";
  net::InetAddress addr(5000, "192.168.1.1");
  LOG_INFO << addr.ToIp();
  LOG_INFO << addr.ToIpPort();
  LOG_INFO << addr.Port();
  LOG_INFO << addr.Ipv4NetEndian();
  LOG_INFO << addr.PortNetEndian();
}

void NewConnetctionFunc(int sockfd, const net::InetAddress& addr) {
  char buff[] = "hello client";
  ssize_t n = ::write(sockfd, buff, strlen(buff));
  printf("write %ld bytes to %s:%u\n", n, addr.ToIp().c_str(), addr.Port());
  ::close(sockfd);
}

BOOST_AUTO_TEST_CASE(Acceptor) {
  std::cout << "-------------Acceptor\n";
  net::EventLoop loop;

  net::Acceptor acceptor(&loop, net::InetAddress(3333), false);
  acceptor.SetNewConnectionCallback(NewConnetctionFunc);
  acceptor.Listen();

  loop.RunAfter(30, std::bind(&net::EventLoop::Quit, &loop));
  Logger::SetLogLevel(Logger::kTrace);
  loop.Loop();
}